package com.ccrfid.rmc6.service.personnel;

import com.ccrfid.rmc6.appconfig.exception.pojo.AppException;
import com.ccrfid.rmc6.appconfig.exception.pojo.CustomErrors;
import com.ccrfid.rmc6.entity.personnel.*;
import com.ccrfid.rmc6.lbservice3.pojo.msg.MsgBloodPressure;
import com.ccrfid.rmc6.lbservice3.pojo.msg.MsgHeartRate;
import com.ccrfid.rmc6.lbservice3.pojo.msg.MsgStepTrack;
import com.ccrfid.rmc6.lbservice3.pojo.msg.MsgTemperature;
import com.ccrfid.rmc6.repository.personnel.*;
import com.ccrfid.rmc6.repository.search.Criterion;
import com.ccrfid.rmc6.repository.search.SearchCriteria;
import com.ccrfid.rmc6.service.sys.OrgService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import javax.ws.rs.core.Response;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class PersonnelVitalSignServiceImpl implements PersonnelVitalSignService {
    private static final Logger logger = LoggerFactory.getLogger(PersonnelVitalSignServiceImpl.class);

    @Autowired
    private PersonnelRepository personnelRepository;
    @Autowired
    private PersonnelViewRepository personnelViewRepository;
    @Autowired
    private PersonnelHeartRateRepository personnelHeartRateRepository;
    @Autowired
    private PersonnelStepTrackRepository personnelStepTrackRepository;
    @Autowired
    private PersonnelTemperatureRepository personnelTemperatureRepository;
    @Autowired
    private PersonnelBloodPressureRepository personnelBloodPressureRepository;
    @Autowired
    private PersonnelFocusRepository personnelFocusRepository;
    @Autowired
    private PersonnelService personnelService;
    @Autowired
    private OrgService orgService;

    @Value("${card.heartRate.max}")
    private Integer maxHeartRate;
    @Value("${card.heartRate.min}")
    private Integer minHeartRate;

    @Override
    public PersonnelHeartRateEntity recordHeartRate(Long personnelId, MsgHeartRate msgHeartRate) {
        PersonnelHeartRateEntity heartRateEntity = new PersonnelHeartRateEntity();
        heartRateEntity.setPersonnelId(personnelId);
        heartRateEntity.setCardNumber(msgHeartRate.getCardNumber());
        heartRateEntity.setHeartRate(msgHeartRate.getHeartRate());
        heartRateEntity.setRecordTime(new Timestamp(msgHeartRate.getTime()));
        return personnelHeartRateRepository.save(heartRateEntity);
    }

    @Override
    public PersonnelStepTrackEntity recordStepTrack(Long personnelId, MsgStepTrack msgStepTrack) {
        PersonnelStepTrackEntity stepTrackEntity = new PersonnelStepTrackEntity();
        stepTrackEntity.setPersonnelId(personnelId);
        stepTrackEntity.setCardNumber(msgStepTrack.getCardNumber());
        stepTrackEntity.setStepCount(msgStepTrack.getStepCount());
        stepTrackEntity.setRecordTime(new Timestamp(msgStepTrack.getTime()));
        return personnelStepTrackRepository.save(stepTrackEntity);
    }

    @Override
    public PersonnelTemperatureEntity recordTemperature(Long personnelId, MsgTemperature msgTemperature) {
        PersonnelTemperatureEntity temperatureEntity = new PersonnelTemperatureEntity();
        temperatureEntity.setPersonnelId(personnelId);
        temperatureEntity.setCardNumber(msgTemperature.getCardNumber());
        temperatureEntity.setTemperature(msgTemperature.getTemperature());
        temperatureEntity.setRecordTime(new Timestamp(msgTemperature.getTime()));
        return personnelTemperatureRepository.save(temperatureEntity);
    }

    @Override
    public PersonnelBloodPressureEntity recordBloodPressure(Long personnelId, MsgBloodPressure msgBloodPressure) {
        PersonnelBloodPressureEntity bloodPressureEntity = new PersonnelBloodPressureEntity();
        bloodPressureEntity.setPersonnelId(personnelId);
        bloodPressureEntity.setCardNumber(msgBloodPressure.getCardNumber());
        bloodPressureEntity.setDiastolicPressure(msgBloodPressure.getDiastolicPressure());
        bloodPressureEntity.setSystolicPressure(msgBloodPressure.getSystolicPressure());
        bloodPressureEntity.setRecordTime(new Timestamp(msgBloodPressure.getTime()));
        return personnelBloodPressureRepository.save(bloodPressureEntity);
    }

    @Override
    public List<PersonnelView> getAllPersonnelsWithVitalSignFocus(String account) {
        List<PersonnelFocusEntity> personnelList = personnelFocusRepository.getAllByAccount(account);
        if (personnelList != null && personnelList.size() > 0) {
            List<Long> idList = personnelList.stream()
                    .mapToLong(personnel -> personnel.getPersonnelId())
                    .boxed()
                    .collect(Collectors.toList());
            List<PersonnelView> resultList = personnelViewRepository.getAllByIdIn(idList);
            return resultList.stream()
                    .filter(entity -> entity.getEnabled().equals(1))
                    .collect(Collectors.toList());
        } else {
            return new ArrayList<>();
        }
    }

    @Override
    public Boolean createVitalSignFocusPersonnel(String account, Long personnelId) throws AppException {
        PersonnelEntity personnelEntity = personnelRepository.getById(personnelId);
        if (personnelEntity != null) {
            PersonnelFocusEntity focusEntity = personnelFocusRepository.getByAccountAndPersonnelId(account, personnelId);
            if (focusEntity != null) {
                focusEntity.setPersonnelName(personnelEntity.getPersonnelName());
                personnelFocusRepository.save(focusEntity);
            } else {
                focusEntity = new PersonnelFocusEntity();
                focusEntity.setAccount(account);
                focusEntity.setPersonnelId(personnelId);
                focusEntity.setPersonnelName(personnelEntity.getPersonnelName());
                personnelFocusRepository.save(focusEntity);
            }
            return true;
        } else {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.person_not_found.getCode(), CustomErrors.person_not_found.getReason(), "");
        }
    }

    @Override
    public Boolean deleteVitalSignFocusPersonnel(String account, Long personnelId) throws AppException {
        if (personnelFocusRepository.existsByAccountAndPersonnelId(account, personnelId)) {
            personnelFocusRepository.deleteAllByAccountAndPersonnelId(account, personnelId);
            return true;
        } else {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.person_not_found.getCode(), CustomErrors.person_not_found.getReason(), "");
        }
    }

    @Override
    public Integer getPersonnelStepCount(Long personnelId, Timestamp startTime, Timestamp stopTime) {
        List<PersonnelStepTrackEntity> trackList =
                personnelStepTrackRepository.getAllByPersonnelIdAndRecordTimeBetween(personnelId, startTime, stopTime);
        if (trackList != null && trackList.size() > 1) {
            List<Integer> stepList = new ArrayList<>();
            for (int i = 0; i < trackList.size() - 1; i++) {
                int left = trackList.get(i).getStepCount();
                int right = trackList.get(i + 1).getStepCount();
                if (right >= left) {
                    stepList.add(right - left);
                } else {
//                    stepList.add(65535 + right - left);
                    stepList.add(right);    //标签断电重启会造成计数归0，不会到达65535
                }
            }
            Optional<Integer> result = stepList.stream().reduce((a, b) -> a + b);
            if (result.isPresent()) {
                return result.get();
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    }

    @Override
    public List<PersonnelHeartRateEntity> getPersonnelHeartRateRecords(Long personnelId, Timestamp startTime, Timestamp stopTime) {
        return personnelHeartRateRepository.getAllByPersonnelIdAndRecordTimeBetween(personnelId, startTime, stopTime);
    }

    @Override
    public List<PersonnelView> getAllPersonnelsWithAbnormalHeartRate(Long orgId, Timestamp checkTime) {
        List<PersonnelView> personnelList = getAllPersonnelByOrgId(orgId);
        if (personnelList != null && personnelList.size() > 0) {
            return personnelList.stream()
                    .filter(personnelView -> isAbnormalHeartRate(personnelView.getId(), checkTime))
                    .collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    private List<PersonnelView> getAllPersonnelByOrgId(Long orgId) {
        SearchCriteria criteria = SearchCriteria.Builder.create();
        criteria.add(new Criterion("enabled", Criterion.Operator.EQ, 1));
        List<Long> idList = orgService.getSubOrgIdList(orgId);
        criteria.add(new Criterion("orgId", Criterion.Operator.IN, idList));

        return personnelService.getPersonnelInfoList(criteria, Sort.by(new ArrayList<>()));
    }

    private boolean isAbnormalHeartRate(Long personnelId, Timestamp checkTime) {
        List<PersonnelHeartRateEntity> list =
                personnelHeartRateRepository.getTop5ByPersonnelIdAndRecordTimeLessThanOrderByIdDesc(personnelId, checkTime);
        if (list != null && list.size() > 0) {
            Optional<Boolean> result = list.stream()
                    .map(entity -> entity.getHeartRate() > maxHeartRate || entity.getHeartRate() < minHeartRate)
                    .reduce((a, b) -> a && b);
            if (result.isPresent()) {
                return result.get();
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    @Override
    public Map<String, Integer> getNormalHeartRateValueRange() {
        Map<String, Integer> map = new HashMap<>();
        map.put("min", minHeartRate);
        map.put("max", maxHeartRate);
        return map;
    }

    @Override
    public PersonnelHeartRateEntity getPersonnelRecentHeartRate(Long personnelId) {
        return personnelHeartRateRepository.getTopByPersonnelIdOrderByIdDesc(personnelId);
    }

    @Override
    public List<PersonnelHeartRateEntity> getPersonnelHeartRateList(Long personnelId,
                                                                    Timestamp startTime, Timestamp stopTime) {
        return personnelHeartRateRepository.getAllByPersonnelIdAndRecordTimeBetween(
                personnelId, startTime, stopTime);
    }

    @Override
    public Integer getPersonnelStepCountData(Long personnelId, Timestamp startTime, Timestamp stopTime) {
        return getPersonnelStepCount(personnelId, startTime, stopTime);
    }

    @Override
    public PersonnelTemperatureEntity getPersonnelRecentTemperature(Long personnelId) {
        return personnelTemperatureRepository.getTopByPersonnelIdOrderByIdDesc(personnelId);
    }

    @Override
    public List<PersonnelTemperatureEntity> getPersonnelTemperatureList(Long personnelId,
                                                                        Timestamp startTime, Timestamp stopTime) {
        return personnelTemperatureRepository.getAllByPersonnelIdAndRecordTimeBetweenOrderByIdAsc(
                personnelId, startTime, stopTime);
    }

    @Override
    public PersonnelBloodPressureEntity getPersonnelRecentBloodPressure(Long personnelId) {
        return personnelBloodPressureRepository.getTopByPersonnelIdOrderByIdDesc(personnelId);
    }

    @Override
    public List<PersonnelBloodPressureEntity> getPersonnelBloodPressureList(Long personnelId,
                                                                            Timestamp startTime, Timestamp stopTime) {
        return personnelBloodPressureRepository.getAllByPersonnelIdAndRecordTimeBetweenOrderByIdAsc(
                personnelId, startTime, stopTime);
    }
}
